package com.microservices.yucloud.api.mongo.service;

import act.controller.annotation.UrlContext;
import act.db.morphia.MorphiaDao;
import com.microservices.yucloud.api.mongo.entity.Order;
import org.osgl.mvc.annotation.GetAction;

import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import java.util.Map;

/**
 * @author zy
 * @date 2019-08-20 20:28
 **/
@UrlContext("order/aggregations")
public class OrderAggregationService {

    @Inject
    private MorphiaDao<Order> dao;

    /**
     * 统计一共有多少订单.
     * <p>
     * 这个可以直接用 Dao 上的 count() 方法, 无需使用聚合管道
     */
    @GetAction("count")
    public long count() {
        return dao.count();
    }

    /**
     * 按照地区统计订单数量
     * <p>
     * 直接构造 MorphiaQuery 对象并使用其 count() 方法
     * 这个方法也无需聚合管道
     */
    @GetAction("/regions/{region}/count")
    public long countByRegion(String region) {
        // 使用字段名构造查询
        return dao.q("region", region).count();
        // 也可以使用 MongoDB column 名字来构造查询:
        // return dao.q("reg", region).count();
    }

    /**
     * 按照部门统计订单数量
     * <p>
     * 直接构造 MorphiaQuery 对象并使用其 count() 方法
     * 这个方法也无需聚合管道
     */
    @GetAction("/departments/{department}/count")
    public long countByDepartment(String department) {
        // 使用字段名构造查询
        return dao.q("department", department).count();
        // 也可以使用 MongoDB column 名字来构造查询:
        // return dao.q("dep", department).count();
    }

    /**
     * 按照地区以及部门统计订单数量
     * <p>
     * 直接构造 MorphiaQuery 对象并使用其 count() 方法
     * 这个方法也无需聚合管道
     */
    @GetAction("/region={region},department={department}/count")
    public long countByRegionAndDepartment(String region, String department) {
        // 使用字段名构造查询
        return dao.q("region,department", region, department).count();
        // 也可以使用 MongoDB column 名字来构造查询:
        // return dao.q("reg,dep", region, department).count();
    }

    /**
     * 一次性获得按地区分组统计订单数量的聚合结果
     * <p>
     * 返回的 Map key 为地区, value 为该地区的订单数量
     */
    @GetAction("/regions/~group-count~")
    public Map<Object, Long> groupCountByRegion() {
        return dao.a().groupCount("region").getAsLongMap();
    }

    /**
     * 一次性获得按部门分组统计订单数量的聚合结果
     * <p>
     * 返回的 Map key 为部门, value 为该部门的订单数量
     */
    @GetAction("/departments/~group-count~")
    public Map<Object, Long> groupCountByDepartment() {
        return dao.a().groupCount("department").getAsLongMap();
    }

    /**
     * 一次性获得按部门分组统计订单数量的聚合结果
     * <p>
     * 返回的 Map key 为地区与部门列表, value 为该地区,部门对应的订单数量
     */
    @GetAction("/~region-department~/~group-count~")
    public Map<Object, Long> groupCountByRegionAndDepartment() {
        return dao.a().groupCount("region,department").getAsLongMap();
    }

    /**
     * 一次性获得部门分组统计小订单数量的聚合结果
     *
     * @param priceTarget - 订单价格, 小于该价格的订单为小订单
     * @return 返回的 Map key 为部门, value 为该部门的订单数量
     */
    @GetAction("/departments/~group-count~/~small-orders~/")
    public Map<Object, Long> groupCountByDepartmentForSmallOrders(int priceTarget) {
        return dao.q("price <", priceTarget).groupBy("department").count().getAsLongMap();
    }

    /**
     * 返回所有订单的价格汇总
     */
    @GetAction("sum")
    public Long sum() {
        return dao.q().longSum("price");
        // 或者这样:
        // return dao.a().groupSum("price", null).getAsLong().val();
    }

    /**
     * 返回所有小订单的价格汇总
     *
     * @param priceTarget - 订单价格, 小于该价格的订单为小订单
     */
    @GetAction("sum/~small-orders~")
    public Long sumSmallOrders(@NotNull Integer priceTarget) {
        return dao.q("price <", priceTarget).longSum("price");
    }

    /**
     * 返回部门订单价格汇总
     *
     * @param department - 指定部门
     */
    @GetAction("/departments/{department}/sum")
    public Long sumByDepartment(String department) {
        return dao.q("department", department).longSum("price");
    }


    /**
     * 返回地区及部门订单价格汇总
     *
     * @param region     - 指定地区
     * @param department - 指定部门
     */
    @GetAction("region={region},department={department}/sum")
    public Long sumByDepartment(String region, String department) {
        return dao.q("region,department", region, department).longSum("price");
    }

    /**
     * 返回按地区分组价格汇总
     *
     * @return Map 的 key 为地区, value 为该地区价格汇总
     */
    @GetAction("regions/~group-sum~")
    public Map<Object, Long> groupSumByRegion() {
        return dao.a().groupSum("price", "region").getAsLongMap();
        // 或者这样
        // return dao.q().groupBy("region").sum("price").getAsLongMap();
    }

    /**
     * 返回按地区分组价格汇总, 过滤掉业绩达标的记录.
     * <p>
     * 注意, 我们这次不能直接在查询中构造过滤条件, 而是使用 SimpleAggregation 的 lessThan 方法,
     * 这是因为查询条件是过滤数据库原始数据的, 而该方法需要过滤汇总后的数据. 简单的说, 查询条件
     * 相当于 SQL 中的 where 子句, 而我们需要的 SQL 的 having 子句.
     *
     * @param priceTarget - 达标业绩
     * @return Map 的 key 为地区, value 为该地区价格汇总
     */
    @GetAction("departments/~group-sum~/~bad-performance~")
    public Map<Object, Long> groupSumByRegionBadPerformance(int priceTarget) {
        return dao.q().groupBy("region").sum("price").lessThan(priceTarget).getAsLongMap();
    }

    /**
     * 返回按地区分组平均价格
     *
     * @return Map 的 key 为地区, value 为该地区平均价格
     */
    @GetAction("regions/~group-average~")
    public Map<Object, Double> groupAverageByRegion() {
        return dao.q().groupBy("region").average("price").getAsMap();
    }

    /**
     * 返回按地区分组价格标准差
     *
     * @return Map 的 key 为地区, value 为该地区标准差
     */
    @GetAction("regions/~group-std-dev~")
    public Map<Object, Double> groupStdDevByRegion() {
        return dao.q().groupBy("region").stdDev("price").getAsMap();
    }

    /**
     * 返回按地区分组价格抽样标准差.
     * <p>
     * 当数据量特别大的时候可以使用抽样统计标准差
     *
     * @param samples 样本数量
     * @return Map 的 key 为地区, value 为该地区抽样标准差
     */
    @GetAction("regions/~group-sample-std-dev~")
    public Map<Object, Double> groupStdDevByRegion(int samples) {
        return dao.q().groupBy("region").sampleStdDev("price", samples).getAsMap();
    }

}

